﻿using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using Microsoft.SharePoint;
using Microsoft.Office.InfoPath.Server.Controls;
using System.Xml;
using System.Collections.Generic;
using System.Xml.XPath;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.Administration;

namespace WUSTL.SharePoint.WebParts.AnonInfoPath.FormViewWebPart
{
    public partial class FormViewWebPartUserControl : UserControl
    {
        public FormViewWebPart WebPartControl { get; set; }
        public int returnCode;
        protected XmlFormView formView = null;
        protected Literal cssReg = null;
        public static string afvwpSubmitUserKey;
        public string wpXsnUri
        {
            get
            {
                if (!String.IsNullOrEmpty(WebPartControl.xsnURI))
                {
                    return WebPartControl.xsnURI;
                }
                else
                {
                    return string.Empty;
                }
            }
        }
        public string wpSubmitLocation
        {
            get
            {
                if (!String.IsNullOrEmpty(WebPartControl.submitLocation))
                {
                    return WebPartControl.submitLocation;
                }
                else
                {
                    return string.Empty;
                }
            }
        }
        public string wpCssLocation
        {
            get
            {
                if (!String.IsNullOrEmpty(WebPartControl.cssLocation))
                {
                    return WebPartControl.cssLocation;
                }
                else
                {
                    return string.Empty;
                }
            }
        }
        public string wpSuccessDdl
        {
            get
            {
                if (!String.IsNullOrEmpty(WebPartControl.successDdl))
                {
                    return WebPartControl.successDdl;
                }
                else
                {
                    return string.Empty;
                }
            }
        }
        public string wpCustomHtml
        {
            get
            {
                if (!String.IsNullOrEmpty(WebPartControl.customHtml))
                {
                    return WebPartControl.customHtml;
                }
                else
                {
                    return string.Empty;
                }
            }
        }
        public string wpRedirectUrl
        {
            get
            {
                if (!String.IsNullOrEmpty(WebPartControl.redirectUrl))
                {
                    return WebPartControl.redirectUrl;
                }
                else
                {
                    return string.Empty;
                }
            }
        }

        public bool wpSendAsEmail
        {
            get
            {
                if (WebPartControl.sendAsEmail != null)
                {
                    return WebPartControl.sendAsEmail;
                }
                else
                {
                    return false;
                }
            }
        }

        public bool wpEnableQueryString
        {
            get
            {
                if (WebPartControl.enableQueryString != null)
                {
                    return WebPartControl.enableQueryString;
                }
                else
                {
                    return false;
                }
            }
        }

        public string wpQueryStringParameter
        {
            get
            {
                if (!String.IsNullOrEmpty(WebPartControl.queryStringParameter))
                {
                    return WebPartControl.queryStringParameter;
                }
                else
                {
                    return string.Empty;
                }
            }
        }

        public string queryStringValue
        {
            get
            {
                if (Request.QueryString[wpQueryStringParameter] != null)
                {
                    return Request.QueryString[wpQueryStringParameter];
                }
                else
                {
                    return string.Empty;
                }
            }
        }

        public string wpQueryStringFormField
        {
            get
            {
                if (!String.IsNullOrEmpty(WebPartControl.queryStringFormField))
                {
                    return WebPartControl.queryStringFormField;
                }
                else
                {
                    return string.Empty;
                }
            }
        }

        public static string userAccount
        {
            get
            {
                SPWeb web;
                SPWeb rootWeb = null;

                //Work with the web
                web = SPContext.Current.Web;

                if (web.IsRootWeb)
                {
                    //Check to see if the key exists
                    afvwpSubmitUserKey = web.ID.ToString() + "_SubmitUser";
                    if (web.AllProperties.ContainsKey(afvwpSubmitUserKey))
                    {
                        //Ensure value is not null or empty
                        if (!String.IsNullOrEmpty(web.AllProperties[afvwpSubmitUserKey].ToString()))
                        {
                            string _user = web.AllProperties[afvwpSubmitUserKey].ToString();
                            return _user;
                        }
                        else
                        {
                            return string.Empty;
                        }
                    }
                    else
                    {
                        return string.Empty;
                    }
                }
                else
                {
                    do
                    {
                        if (web.IsRootWeb)
                        {
                            rootWeb = web;
                        }
                        else
                        {
                            web = web.ParentWeb;
                            rootWeb = null;
                        }
                    }
                    while (rootWeb == null);
                    afvwpSubmitUserKey = rootWeb.ID.ToString() + "_SubmitUser";
                    if (rootWeb.AllProperties.ContainsKey(afvwpSubmitUserKey))
                    {
                        //Ensure value is not null or empty
                        if (!String.IsNullOrEmpty(web.AllProperties[afvwpSubmitUserKey].ToString()))
                        {
                            string _user = rootWeb.AllProperties[afvwpSubmitUserKey].ToString();
                            rootWeb.Dispose();
                            return _user;
                        }
                        else
                        {
                            rootWeb.Dispose();
                            return string.Empty;
                        }
                    }
                    else
                    {
                        rootWeb.Dispose();
                        return string.Empty;
                    }
                }
            }
        }

        protected override void CreateChildControls()
        {
            base.CreateChildControls();

            //Add an XmlFormView control to the page
            formView = new XmlFormView();
            formView.Width = Unit.Percentage(100);
            formViewPH.Controls.Add(formView);
        }

        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);

            string xsnURI;

            //Make all panels non-visible
            foreach (Control p in this.Controls)
            {
                if (p is Panel)
                {
                    p.Visible = false;
                }
            }

            //Add a CssRegistration, if need be 
            if (!string.IsNullOrEmpty(wpCssLocation))
            {
                cssReg = new Literal();
                cssReg.Visible = true;
                cssReg.Text = "<link rel='stylesheet' type='text/css' href='";
                cssReg.Text += ParseUrl(wpCssLocation);
                cssReg.Text += "'/>";
                cssPH.Controls.Add(cssReg);
            }

            //Make sure the xsnURI is set and parse it.
            if (String.IsNullOrEmpty(wpXsnUri))
            {
                xsnURI = string.Empty;
            }
            else
            {
                xsnURI = ParseUrl(wpXsnUri);
            }

            if (!String.IsNullOrEmpty(xsnURI))
            {
                string submitLocationType = IsListOrLibrary(xsnURI);
                if (!String.IsNullOrEmpty(submitLocationType))
                {
                    xsnURI = xsnURI;
                }
                else
                {
                    xsnURI = string.Empty;
                }
            }

            //Set up the formView
            formView.XsnLocation = xsnURI;
            formView.ShowHeader = false;
            formView.ShowFooter = false;
            formView.EditingStatus = XmlFormView.EditingState.Editing;
            formView.Initialize += new EventHandler<InitializeEventArgs>(formView_Initialize);
            formView.SubmitToHost += new EventHandler<SubmitToHostEventArgs>(formView_SubmitToHost);

            //Display a message if the webpart is not configured, otherwise display the formview
            var wp = WebPartManager.GetCurrentWebPartManager(this.Page);
            if (wp.DisplayMode == WebPartManager.BrowseDisplayMode)
            {
                if (formView != null)
                {
                    if (String.IsNullOrEmpty(formView.XsnLocation) || String.IsNullOrEmpty(wpSubmitLocation))
                    {
                        noConfigDisplayPnl.Visible = true; 
                    }
                    else
                    {
                        formViewPnl.Visible = true;
                    }
                }
            }
            else
            {
                if (formView != null)
                {
                    if (String.IsNullOrEmpty(formView.XsnLocation) || String.IsNullOrEmpty(wpSubmitLocation))
                    {
                        noConfigEditPnl.Visible = true;
                    }
                    else
                    {
                        formViewPnl.Visible = true;
                    }
                }
            }
        }

        void formView_Initialize(object sender, InitializeEventArgs e)
        {
            if (wpEnableQueryString)
            {
                SetFieldValue();
            }
        }

        void formView_SubmitToHost(object sender, SubmitToHostEventArgs e)
        {
            try
            {
                //Make sure the submitLocation is set and parse it.
                string submitUrl;
                if (!String.IsNullOrEmpty(wpSubmitLocation))
                {
                    submitUrl = ParseUrl(wpSubmitLocation);
                }
                else
                {
                    submitUrl = string.Empty;
                }

                if (!string.IsNullOrEmpty(submitUrl))
                {
                    //Elevate to System Account, then call AddFile()
                    SPSecurity.RunWithElevatedPrivileges(delegate
                    {
                        returnCode = AddFile(submitUrl, SerializeForm(formView));
                    });

                    if (returnCode == 1)
                    { SuccessAction(); }

                    else
                    {
                        Exception ex = new Exception("An unknown error has occurred. Submission has failed.");
                        throw ex;
                    }
                }
                else
                {
                    noConfigDisplayPnl.Visible = true;
                }
            }
            catch (Exception ex)
            {
                WriteToULSLogs(ex);
            }
        }

        /// <summary>
        /// Submit to a Folder
        /// </summary>
        /// <param name="url"></param>
        /// <param name="values"></param>
        /// <param name="formData"></param>
        /// <returns></returns>
        public static int AddFile(string url, Byte[] formData)
        {
            if ((url != null) && (url.Length != 0))
            {
                using (SPSite site = new SPSite(url))
                {
                    using (SPWeb web = site.OpenWeb())
                    {
                        try
                        {
                            SPUserToken userToken = web.AllUsers[userAccount].UserToken;
                            using (SPSite s = new SPSite(url, userToken))
                            {
                                using (SPWeb w = s.OpenWeb())
                                {
                                    try
                                    {
                                        w.AllowUnsafeUpdates = true;
                                        SPFolder submitLib = w.GetFolder(url);
                                        string libUrl = submitLib.ServerRelativeUrl;
                                        libUrl = libUrl.Replace("/Forms/AllItems.aspx", "");
                                        libUrl = libUrl + "/" + DateTime.UtcNow.Ticks.ToString() + ".xml";
                                        SPFile file = submitLib.Files.Add(libUrl, formData, false);
                                        w.AllowUnsafeUpdates = false;
                                        return 1;

                                    }
                                    finally
                                    {
                                        w.AllowUnsafeUpdates = false;
                                        if (w != null) { w.Dispose(); }
                                        if (s != null) { s.Dispose(); }
                                        if (web != null) { web.Dispose(); }
                                        if (site != null) { site.Dispose(); }
                                    }
                                }
                            }
                        }
                        finally
                        {
                            if (web != null) { web.Dispose(); }
                            if (site != null) { site.Dispose(); }
                        }
                    }
                }
            }
            else
            {
                Exception ex = new Exception("Invalid Submit Location URL");
                throw ex;
            }
        }

        /// <summary>
        /// Returns the absolute path or an empty string.
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public static string ParseUrl(string url)
        {
            if (String.IsNullOrEmpty(url))
            {
                return String.Empty;
            }
            else
            {
                if (url.Contains("http://") ||
                    url.Contains("https://"))
                {
                    if (url.Contains("/Forms/AllItems.aspx"))
                    {
                        url.Replace("/Forms/AllItems.aspx", string.Empty);
                        return url;
                    }
                    else
                    {
                        return url;
                    }
                }
                else
                {
                    if (url.Contains("/Forms/AllItems.aspx"))
                    {
                        url.Replace("/Forms/AllItems.aspx", string.Empty);
                        return SPContext.Current.Site.Url + url;
                    }
                    else
                    {
                        return SPContext.Current.Site.Url + url;
                    }
                }
            }
        }

        /// <summary>
        /// Encode the submitted XML from an XmlFormView into a Byte Array.
        /// </summary>
        /// <param name="fv"></param>
        /// <returns></returns>
        public static Byte[] SerializeForm(XmlFormView fv)
        {
            //Grab the entire XML file and encode it into a byte array
            XPathNavigator fullNav = fv.XmlForm.MainDataSource.CreateNavigator();
            return System.Text.Encoding.UTF8.GetBytes(fullNav.OuterXml);
        }

        /// <summary>
        /// Add the exception to the ULS logs
        /// </summary>
        /// <param name="capEx"></param>
        protected void WriteToULSLogs(Exception capEx)
        {
            SPSecurity.RunWithElevatedPrivileges(delegate()
            {
                try
                {
                    string shortErrorMessage = capEx.Message;
                    string longErrorMessage = capEx.ToString();
                    Exception innerEx = capEx.InnerException;

                    //Process any inner exceptions
                    while (innerEx != null)
                    {
                        longErrorMessage += Environment.NewLine + "Inner Exception:" + Environment.NewLine + innerEx.ToString();
                    }

                    //Put the message in the on-page literal
                    printExceptionLit.Text = longErrorMessage;

                    //Make other panels non-visible, enable visibility on the error panel
                    foreach (Control p in this.Controls)
                    {
                        if (p is Panel)
                        {
                            p.Visible = false;
                        }
                    }
                    failurePnl.Visible = true;
                    printExceptionLit.Visible = true;

                    //Add item to ULS logs
                    SPDiagnosticsService.Local.WriteTrace(7568,
                        new SPDiagnosticsCategory("Anon InfoPath WebPart Error", TraceSeverity.Unexpected, EventSeverity.Error),
                        TraceSeverity.Unexpected, longErrorMessage);
                }
                catch
                {
                    //Logging failed. Whoops!
                }
            });
        }

        /// <summary>
        /// Perform any post-submit success actions
        /// </summary>
        protected void SuccessAction()
        {
            string action = wpSuccessDdl;

            if (wpSendAsEmail) { SendEmail(); }

            if (action == "Generic")
            {
                formViewPnl.Visible = false;
                successGenericPnl.Visible = true;
            }

            if (action == "Custom")
            {
                if (!String.IsNullOrEmpty(wpCustomHtml))
                {
                    formViewPnl.Visible = false;
                    customSuccess.Text = wpCustomHtml;
                    customSuccess.Visible = true;
                    successCustomPnl.Visible = true;
                }

                else
                {
                    formViewPnl.Visible = false;
                    successGenericPnl.Visible = true;
                }
            }

            if (action == "Redirect")
            {
                if (!String.IsNullOrEmpty(wpRedirectUrl))
                {
                    Page.Response.Redirect(wpRedirectUrl);
                }

                else
                {
                    formViewPnl.Visible = false;
                    successGenericPnl.Visible = true;
                }
            }

            if (action == "No Action")
            {
                //Do nothing. Probably a useless condition.
            }
        }

        protected void SetFieldValue()
        {
            if (!String.IsNullOrEmpty(wpQueryStringFormField) && !String.IsNullOrEmpty(queryStringValue))
            {
                //Make sure we have the namespace
                XmlNamespaceManager manager = formView.XmlForm.NamespaceManager;

                //Grab the my:myFields node from the XML Doc
                XPathNavigator xPathNav = formView.XmlForm.MainDataSource.CreateNavigator().SelectSingleNode(
                    "my:myFields", manager);

                //Grab the node and set the value
                xPathNav.SelectSingleNode("//my:" + wpQueryStringFormField, manager).SetValue(queryStringValue);
            }
        }

        /// <summary>
        /// Send an email
        /// </summary>
        protected void SendEmail()
        {
            string emailTo;
            string emailFrom;
            string emailSubject;
            string emailBody;

            //Instantiate a nsmgr
            XmlNamespaceManager manager = formView.XmlForm.NamespaceManager;

            //Grab the my:myFields node of the XML Document
            XPathNavigator xPathNav = formView.XmlForm.MainDataSource.CreateNavigator().SelectSingleNode(
                "my:myFields", manager);

            //Grab email data
            emailTo = xPathNav.SelectSingleNode("//my:emailTo", manager).Value;
            emailFrom = xPathNav.SelectSingleNode("//my:emailFrom", manager).Value;
            emailSubject = xPathNav.SelectSingleNode("//my:emailSubject", manager).Value;
            emailBody = xPathNav.SelectSingleNode("//my:emailBody", manager).Value;

            //Prepare the email header
            var messageHeader = new System.Collections.Specialized.StringDictionary();
            messageHeader.Add("to", emailTo);
            messageHeader.Add("from", emailFrom);
            messageHeader.Add("subject", emailSubject);
            messageHeader.Add("content-type", "text/html");

            //Send the email
            Microsoft.SharePoint.Utilities.SPUtility.SendEmail(
                SPContext.Current.Web, messageHeader, emailBody);
        }

        /// <summary>
        /// Use to detect whether a url refers to an SPList, SPFolder, or is invalid.
        /// </summary>
        /// <param name="url">Absolute URL path to be checked.</param>
        /// <returns></returns>
        public static string IsListOrLibrary(string url)
        {
            string validUrl = ParseUrl(url);

            if (String.IsNullOrEmpty(validUrl))
            {
                Exception ex = new Exception("Invalid Submit Location URL");
                throw ex;
            }

            if (validUrl.Contains("/Lists/"))
            {
                if (ListExists(validUrl))
                {
                    //URL is valid, list exists.
                    return "List";
                }
                else
                {
                    return String.Empty;
                }
            }
            else
            {
                if (FolderExists(validUrl))
                {
                    //URL is valid, folder exists.
                    return "Library";
                }
                else
                {
                    return String.Empty;
                }
            }
        }

        /// <summary>
        /// Ensure the list exists before attempting to use it.
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public static bool ListExists(string url)
        {
            using (SPSite site = new SPSite(url))
            {
                using (SPWeb web = site.OpenWeb())
                {
                    try
                    {
                        SPList list = web.GetList(url);
                        return true;
                    }
                    finally
                    {
                        if (web != null) { web.Dispose(); }
                        if (site != null) { site.Dispose(); }
                    }
                }
            }
        }

        /// <summary>
        /// Ensure the folder exists before attempting to use it.
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public static bool FolderExists(string url)
        {
            using (SPSite site = new SPSite(url))
            {
                using (SPWeb web = site.OpenWeb())
                {
                    try
                    {
                        SPFolder folder = web.GetFolder(url);
                        return true;
                    }
                    finally
                    {
                        if (web != null) { web.Dispose(); }
                        if (site != null) { site.Dispose(); }
                    }
                }
            }
        }

        protected void Page_Load(object sender, EventArgs e)
        {
            EnsureChildControls();
        }

        #region NYI - Not Yet Implemented
        ///// <summary>
        ///// Submit to a list.
        ///// </summary>
        ///// <param name="url"></param>
        ///// <param name="values"></param>
        ///// <returns></returns>
        //public static int AddItem(string url, Dictionary<string, string> values)
        //{
        //    if ((url != null) && (url.Length != 0))
        //    {
        //        using (SPSite site = new SPSite(url))
        //        {
        //            using (SPWeb web = site.OpenWeb())
        //            {
        //                try
        //                {
        //                    SPUserToken userToken = web.AllUsers[userName].UserToken;
        //                    using (SPSite s = new SPSite(url, userToken))
        //                    {
        //                        using (SPWeb w = s.OpenWeb())
        //                        {
        //                            try
        //                            {
        //                                s.CatchAccessDeniedException = false;
        //                                w.AllowUnsafeUpdates = true;
        //                                SPList list = w.GetList(url);
        //                                SPListItem listItem = list.Items.Add();
        //                                foreach (string key in values.Keys)
        //                                {
        //                                    listItem[key] = values[key];
        //                                }
        //                                listItem.Update();
        //                                w.AllowUnsafeUpdates = false;
        //                                return 1;
        //                            }
        //                            finally
        //                            {
        //                                w.AllowUnsafeUpdates = false;
        //                                if (w != null) { w.Dispose(); }
        //                                if (s != null) { s.Dispose(); }
        //                                if (web != null) { web.Dispose(); }
        //                                if (site != null) { site.Dispose(); }
        //                            }
        //                        }
        //                    }
        //                }
        //                finally
        //                {
        //                    if (web != null) { web.Dispose(); }
        //                    if (site != null) { site.Dispose(); }
        //                }
        //            }
        //        }
        //    }
        //    else
        //    {
        //        Exception ex = new Exception("Invalid Submit Location URL");
        //        throw ex;
        //    }
        //}

        ///// <summary>
        ///// Grab nodes contained within the my:myFields node of an XmlFormView.
        ///// </summary>
        ///// <param name="fv"></param>
        ///// <returns></returns>
        //public static XmlNodeList GrabNodes(XmlFormView fv)
        //{
        //    //Instantiate a nsmgr
        //    XmlNamespaceManager manager = fv.XmlForm.NamespaceManager;

        //    //Grab the my:myFields node of the XML Document
        //    XPathNavigator xPathNav = fv.XmlForm.MainDataSource.CreateNavigator().SelectSingleNode(
        //        "my:myFields", manager);

        //    //Grab the child nodes of my:submitFields
        //    XmlDocument doc = new XmlDocument();
        //    doc.LoadXml(xPathNav.InnerXml);
        //    XmlElement root = doc.DocumentElement;
        //    return root.SelectNodes("my:submitFields", manager);
        //}
        #endregion
    }
}